#include "./conn_port.h"
#include "../utils/log.h"
#include "./conn_impl.h"
#include "../app.h"


namespace conn {

ConnPort::ConnPort() : 
    m_ctx(1)
{

}

ConnPort::~ConnPort() {
    close();
}

void ConnPort::close() {
    m_write_mq.interrupt();
    if (m_port->is_open()) {
        std::error_code ec;
        m_port->close(ec);
    }
    
    if (m_reader.joinable()) {
        m_reader.join();
    }
    if (m_writer.joinable()) {
        m_writer.join();
    }
}

void ConnPort::open(const QString &name, int baud_rate) {
    close();
    m_write_mq.reset();

    m_port = std::make_unique<asio::serial_port>( m_ctx, name.toStdString() );
    m_port->set_option(serial_port::baud_rate(baud_rate));
    m_port->set_option(serial_port::character_size(8));
    m_port->set_option(serial_port::parity());
    m_port->set_option(serial_port::flow_control());
    m_port->set_option(serial_port::stop_bits());

    m_reader = std::thread(&ConnPort::recv_task, this);
    m_writer = std::thread(&ConnPort::write_task, this);
}

void ConnPort::recv_task() {
    for (;;) {
        try {
            utils::ByteBuf buf = recv(*m_port.get());
            dispatch(std::move(buf));
        } catch (const utils::ProtoException &e) {
            log_printf("proto:%s", e.what());
            continue;
        } catch (const std::exception &e) {
            log_printf("r err:%d", e.what());
            break;
        }
    }
    log_printf("read exit");
}

void ConnPort::write_task() {
    for (;;) {
        try {
            utils::ByteBuf buf = m_write_mq.take();
            asio::write(*m_port.get(), asio::buffer(buf.buf(), buf.len()));
        } catch (const std::exception &e) {
            log_printf("w err:%s", e.what());
            break;
        }
    }
    log_printf("write exit");
}

void ConnPort::dispatch(utils::ByteBuf&& buf) {
    uint8_t type = buf[0];

    if ((type == proto::type::RES) || (type == proto::type::NOTIFY_ACK)) {
        //write_ack;
    }

    switch (type) {
        case proto::type::ACK:
        case proto::type::PONG:
        case proto::type::SIMPLE_RES:
        case proto::type::RES:
            run_on_ui([this, b = std::move(buf)] () mutable {
                m_resp_map.handle_res(std::move(b));
            });
        return;
    }

    if ((type == proto::type::NOTIFY) || (type == proto::type::NOTIFY_ACK)) {
        auto shared_buf = std::make_shared< utils::ByteBuf >(std::move(buf));
        emit notify(shared_buf);
    }
}

}















